#ifndef __LCG_SINK_H__
#define __LCG_SINK_H__
/*  日志落地模块
        1.抽象落地类
        2.派生子类（根据不同的落地方向进行派生）
        3.使用工厂模式进行创建和表示的分离
*/
#include "util.hpp"

#include <memory>
#include <fstream>
#include <cassert>
#include <sstream>

namespace lcglog{
    //1.抽象落地基类
    class LogSink{
        public:
            using ptr = std::shared_ptr<LogSink>;//使用智能指针管理LogSink对象
            LogSink(){}
            virtual ~LogSink(){}
            virtual void log(const char* data, size_t len) = 0;
    };
    //2.派生不同的落地方向
    //落地方向：标准输出
    class StdoutSink : public LogSink{
        public:
            //将日志消息写入到标准输出
            //不要直接使用std::cout<<进行输出，因为输出的不一定是字符串
            void log(const char* data, size_t len) override{
                std::cout.write(data, len);
            }
    };
    //落地方向：指定文件
    class FileSink : public LogSink{
        public:
            //构造时将文件名传入，并且打开文件，将操作句柄管理起来，通过文件句柄管理文件
            FileSink(const std::string& pathname):_pathname(pathname){
                //1.创建日志文件（pathname)所在的目录
                util::File::createDirectory(util::File::path(pathname));
                //2.创建并打开日志文件
                _ofs.open(pathname, std::ios::binary | std::ios::app);
                assert(_ofs.is_open());
            }
            //将日志消息写入到指定文件
            void log(const char* data, size_t len) override{
                _ofs.write(data, len);
                _ofs.good();
            }
        private:
            std::string _pathname;  //文件名
            std::ofstream _ofs;     //操作句柄
    };
    //落地方向：滚动文件(以大小进行滚动)
    class RollBySizeSink : public LogSink{
        public:
            //构造时将文件名传入，并且打开文件，将操作句柄管理起来，通过文件句柄管理文件
            RollBySizeSink(const std::string& basename, size_t max_size)
            :_basename(basename), _max_fsize(max_size), _cur_fsize(0), _name_size(0){
                std::string pathname = createNewFile();
                //1.创建日志文件（pathname)所在的目录
                util::File::createDirectory(util::File::path(pathname));
                //2.创建并打开日志文件
                _ofs.open(pathname, std::ios::binary | std::ios::app);
                assert(_ofs.is_open());
            }
            //将日志消息写入到指定文件--写入前要判断文件大小，超过了文件最大的大小就要切换文件
            void log(const char* data, size_t len) override{
                if(_cur_fsize + len >= _max_fsize){
                    _ofs.close();//关闭原来已经打开的文件
                    std::string filename = createNewFile();//创建新文件
                    _ofs.open(filename, std::ios::binary | std::ios::app);
                    assert(_ofs.is_open());
                    
                    _cur_fsize = 0;
                }

                _ofs.write(data, len);
                _cur_fsize += len;
                assert(_ofs.good());
            }
        private:
            //进行大小判断，超过指定大小则创建新文件
            std::string createNewFile(){

                //获取系统时间，以时间来构建文件拓展名
                time_t t = util::Date::now();
                struct tm lt;
                localtime_r(&t, &lt);
                std::stringstream filename;
                filename << _basename;
                filename << lt.tm_year+1900;
                //filename << "-";
                filename << lt.tm_mon+1;
                //filename << "-";
                filename << lt.tm_mday;
                //filename << " ";
                filename << lt.tm_hour;
                //ilename << ":";
                filename << lt.tm_min;
                //filename << ":";
                filename << lt.tm_sec;
                filename << "-";
                filename << _name_size++;
                filename << ".log";

                return filename.str();
            }
        private:
            //通过基础文件名+拓展文件名（以时间生成）,组成一个实际的当前输出文件名
            std::string _basename;// ./logs/base-  ./logs/base-20101210091232.log
            std::ofstream _ofs; //操作句柄
            size_t _max_fsize;  //记录最大大小，当前文件超过了这个大小就要切换文件
            size_t _cur_fsize;  //记录当前文件已经写入的数据大小

            size_t _name_size;
    };

    //将不同的落地方法设计为简单工厂模式
    class SinKFactory{
        public:
            template<typename SinkType, typename... Args>
                static LogSink::ptr create(Args &&...args){
                    return std::make_shared<SinkType>(std::forward<Args>(args)...);
                }
    };
}
#endif